#include "opengles2.h"

GLuint loadShader(GLenum shaderType, const char* pSource)
{
	GLuint shader = glCreateShader(shaderType);
	if (shader)
	{
		glShaderSource(shader, 1, &pSource, NULL);
		glCompileShader(shader);
		GLint compiled = 0;
		glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
		if (!compiled)
		{
			GLint infoLen = 0;
			glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
			if (infoLen)
			{
				char* buf = (char*) malloc(infoLen);
				if (buf)
				{
					glGetShaderInfoLog(shader, infoLen, NULL, buf);
					LOGE("Could not compile shader %d:\n%s\n",
						shaderType, buf);
					free(buf);
				}
				glDeleteShader(shader);
				shader = 0;
			}
		}
	}
	return shader;
}

GLuint createProgram(const char* pVertexSource, const char* pFragmentSource, GLuint *vertexShader, GLuint *fragmentShader)
{
	*vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
	if (!(*vertexShader))
	{
		LOGE("loadShader GL_VERTEX_SHADER fail!");
		return 0;
	}

	*fragmentShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
	if (!(*fragmentShader))
	{
		LOGE("loadShader GL_FRAGMENT_SHADER fail!");
		return 0;
	}

	GLuint program = glCreateProgram();
	if (program)
	{
		glAttachShader(program, *vertexShader);
		checkGlError("glAttachShader");
		glAttachShader(program, *fragmentShader);
		checkGlError("glAttachShader");
		glLinkProgram(program);
		GLint linkStatus = GL_FALSE;
		glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
		if (linkStatus != GL_TRUE)
		{
			GLint bufLength = 0;
			glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
			if (bufLength)
			{
				char* buf = (char*) malloc(bufLength);
				if (buf)
				{
					glGetProgramInfoLog(program, bufLength, NULL, buf);
					LOGE("Could not link program:\n%s\n", buf);
					free(buf);
				}
			}
			glDeleteProgram(program);
			program = 0;
		}
	}
	return program;
}

void deleteProgram(GLuint program, GLuint vertexShader, GLuint fragmentShader)
{
	if (vertexShader)
	{
		glDeleteShader(vertexShader);
	}

	if (fragmentShader)
	{
		glDeleteShader(fragmentShader);
	}

	if (program)
	{
		glDeleteProgram(program);
	}
	glReleaseShaderCompiler();
}

GLint getUniformLocation(GLuint program, const char *uniformName)
{
	GLint maxUniformLen;
	GLint numUniforms;
	char *maxUniformName;
	GLint index;
	GLint size;
	GLenum type;
	GLint location;

	glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &numUniforms);
	glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformLen);
	maxUniformName = (char *)malloc(sizeof(char) * maxUniformLen);
	if (!maxUniformName)
	{
		return 0;
	}

	location = 0;
	for(index = 0; index < numUniforms; index++)
	{
		bzero(maxUniformName, sizeof(char) * maxUniformLen);
		glGetActiveUniform(program, index, maxUniformLen, NULL, &size, &type, maxUniformName);
		if (strcmp(uniformName, maxUniformName) == 0)
		{
			location = glGetUniformLocation(program, uniformName);
			break;
		}
	}
	free(maxUniformName);
	return location;
}
